package com.fdb.basic.framework.web.controller;

import static java.nio.file.Files.copy;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

import com.irdstudio.basic.framework.core.constant.ResponseData;
import com.irdstudio.basic.framework.core.util.StringUtil;

/**
 * @description:具有上传下载功能的Controller，各个需要上传下载的Controller可继承此类</br>
 * 				需要实现<code>uploadPath()</code>方法，提供上传路径。</br>
 * 				需要实现<code>getFiles(String str)</code>方法，用以提供下载的源文件
 * @date:2018年2月20日 下午2:19:33
 * @author:hengyh
 */
public abstract class UploadAndDownloadSupport extends AbstractController {

	/* 文件上传下载成功状态码值 */
	public static final String FILE_SUCCESS = "S"; 
	/* 文件上传下载失败状态码值 */
	public static final String FILE_FAILED = "F";
	/* 文件上传的属性key值 */
	public static final String FILE_UPLOAD_NAME = "file";
	/* 文件上传的请求路径 */
	public static final String UPLOAD_REQUEST_URL = "/api/upload";
	/* 文件下载的请求路径 */
	public static final String DOWNLOAD_REQUEST_URL = "/api/download";
	
	public UploadAndDownloadSupport() {
		super();
		File file = new File(uploadPath());
		validateUploadPathAndThrow(file);
		uploadPath = file;
	}
	
	/*------------------------上传模块----------------------*/
	/* 上传路径，需子类提供 */
	private final File uploadPath;
	
	/**
	 * @description:上传文件到指定目录下
	 */
	@PostMapping(UPLOAD_REQUEST_URL)
	public ResponseData<String> upload(@RequestPart(FILE_UPLOAD_NAME) MultipartFile file) {
		
		String fileName = file.getOriginalFilename();
		if (StringUtil.isNullorBank(fileName)) {
			fileName = file.getName();
		}
		File destFile = new File(uploadPath.getAbsolutePath() + File.separator + fileName);
		
		try {
			file.transferTo(destFile);
		} catch (IllegalStateException | IOException e) {
			handleUploadException(e);
			return getResponseData(FILE_FAILED);
		}
		
		return getResponseData(FILE_SUCCESS);
	}
	

	/**
	 * @description:校验上传路径,子类可覆盖
	 */
	protected void validateUploadPathAndThrow(File dest) 
			throws IllegalArgumentException {
		if (!dest.exists()) {
			throw new IllegalArgumentException(
					"文件上传路径：" + dest.getPath() + "不存在！");
		}
		if (!dest.isDirectory()) {
			throw new IllegalArgumentException(
					"文件上传路径：" + dest.getPath() + "配置错误， 必须为目录类型！");
		}
	}
	
	/**
	 * @description:处理上传异常，子类可覆盖
	 */
	protected void handleUploadException(Exception e) { logger.error("上传文件出错!", e); }
	
	/**
	 * @description:子类需实现此方法以提供上传路径,必须是dir路径类型！
	 */
	protected abstract String uploadPath();
	
	/*------------------------上传模块----------------------*/
	
	/*------------------------下载模块----------------------*/
	/**
	 * @description:根据请求下载文件
	 */
	@PostMapping(DOWNLOAD_REQUEST_URL)
	public ResponseData<String> download(@RequestBody String requeString, HttpServletResponse response) {
		List<File> files = getFiles(requeString);
		try {
			for (File file : files) {
				doDownLoad(file, response.getOutputStream());
			}
		} catch (Exception e) {
			handleDownloadException(e);
			return getResponseData(FILE_FAILED);
		}
		return null;
	}
	
	protected void doDownLoad(File file, OutputStream outputStream) throws IOException {
		copy(file.toPath(), outputStream);
	};
	
	/**
	 * @description:处理下载失败逻辑， 子类可复写该方法
	 */
	protected void handleDownloadException(Exception e) { logger.error(e.getMessage()); }
	
	/**
	 * @description:子类需提供根据rest请求的字符串参数获得下载源文件的实现
	 */
	protected abstract List<File> getFiles(String str);
	/*------------------------下载模块----------------------*/
}	
